﻿using UnityEngine;
using System.Collections.Generic;

public class AStarClient : MonoBehaviour
{
    static ObjectPool<HexCellPoint> hexcellPool = null;//使用对象池，避免频繁创建对象

    List<Vector2Int> neighborCells = new List<Vector2Int>(6);//可行邻居格最多6个

    HashSet<Vector2Int> closeList;
    Heap<HexCellPoint> openList;

    private void Start()
    {
        int t = HexagnoalMap.Instance.MaxCoor;
        t = (t * 2 + 5) * t;
        openList = new Heap<HexCellPoint>(HexCellPoint.Compare, t);//比全部网格稍少一点
        closeList = new HashSet<Vector2Int>(Vector2IntComparer.GetComperer());
        if (hexcellPool == null)
            hexcellPool = new ObjectPool<HexCellPoint>(() => new HexCellPoint(), t);
    }

    public bool GetPath(Vector2Int start, Vector2Int end,Stack<Vector2Int> results)
    {
        closeList.Clear();
        openList.Clear();
        HexCellPoint temp = hexcellPool.GetObject();
        temp.Coordinate = start;
        temp.H = HexagnoalMap.Distance(start, end);
        temp.Parent = null;
        openList.Add(temp);
        Vector2IntComparer comparer = Vector2IntComparer.GetComperer();
        HexCellPoint temp2 = null, temp2OriParent;
        int oidx;
        while (openList.Count > 0)
        {
            temp = openList.RemoveTop();
            GetNeighbortPoint(temp);
            for (int i = 0; i < neighborCells.Count; i++)
            {
                if (comparer.Equals(end, neighborCells[i])) //找到终点
                {
                    temp2 = hexcellPool.GetObject();
                    temp2.Parent = temp;
                    temp2.Coordinate = end;
                    goto ok;
                }             
                if (!OpenListContains(neighborCells[i], out oidx))
                {
                    temp2 = hexcellPool.GetObject();
                    temp2.Parent = temp;
                    temp2.Coordinate = neighborCells[i];
                    temp2.CaculateG();
                    temp2.H = HexagnoalMap.Distance(end, neighborCells[i]);
                    openList.Add(temp2); 
                }
                else //开启列表包含该点，检测是否需要更换父节点
                {
                    temp2 = openList[oidx];
                    temp2OriParent = temp2.Parent;
                    int orig = temp2.G;
                    temp2.Parent = temp;
                    temp2.CaculateG();
                    if (temp2.G >= orig)  //原本的G值更小，恢复原状
                    {
                        temp2.Parent = temp2OriParent;
                        temp2.G = orig;
                    }
                    else
                        openList.MoveUp(oidx);
                }
            }
            closeList.Add(temp.Coordinate);
           // openList.ReBuildHeap();//重建堆，效率反而更低
        }
        return false;
        //return null; //其实也可以不返回null，这样就可以来到离不可达点周围最近的某个点
        ok:
        while (temp2.Parent != null)//这样就不会把起点也放进去
        {
            results.Push(temp2.Coordinate);
            hexcellPool.RecircleObject(temp2);
            temp2 = temp2.Parent;
        }
        return true;
        //return results;
    }
    //获取当前单元格所有可行邻居格
    void GetNeighbortPoint(HexCellPoint point)
    {
        neighborCells.Clear();
        Vector2Int temp = point.Coordinate;
        //右上
        temp.y -= 1;
        int z = -temp.x - temp.y;
        int maxcoor = HexagnoalMap.Instance.MaxCoor;
        if (Mathf.Abs(temp.y) <= maxcoor && Mathf.Abs(z) <= maxcoor && HexagnoalMap.Instance.IsReachablePoint(temp) && !closeList.Contains(temp))
        {
            neighborCells.Add(temp);
        }
        //右
        temp = point.Coordinate;
        temp.y -= 1;temp.x += 1;
        if (Mathf.Abs(temp.y) <= maxcoor && Mathf.Abs(temp.x) <= maxcoor && HexagnoalMap.Instance.IsReachablePoint(temp) && !closeList.Contains(temp))
        {
            neighborCells.Add(temp);
        }
        //右下
        temp = point.Coordinate;
        temp.x += 1;
        z = -temp.x - temp.y;
        maxcoor = HexagnoalMap.Instance.MaxCoor;
        if (Mathf.Abs(temp.x) <= maxcoor && Mathf.Abs(z) <= maxcoor && HexagnoalMap.Instance.IsReachablePoint(temp) && !closeList.Contains(temp))
        {
            neighborCells.Add(temp);
        }
        //左下
        temp = point.Coordinate;
        temp.y += 1;
        z = -temp.x - temp.y;
        maxcoor = HexagnoalMap.Instance.MaxCoor;
        if (Mathf.Abs(temp.y) <= maxcoor && Mathf.Abs(z) <= maxcoor && HexagnoalMap.Instance.IsReachablePoint(temp) && !closeList.Contains(temp))
        {
            neighborCells.Add(temp);
        }
        //左
        temp = point.Coordinate;
        temp.y += 1; temp.x -= 1;
        if (Mathf.Abs(temp.y) <= maxcoor && Mathf.Abs(temp.x) <= maxcoor && HexagnoalMap.Instance.IsReachablePoint(temp) && !closeList.Contains(temp))
        {
            neighborCells.Add(temp);
        }
        //左上
        temp = point.Coordinate;
        temp.x -= 1;
        z = -temp.x - temp.y;
        maxcoor = HexagnoalMap.Instance.MaxCoor;
        if (Mathf.Abs(temp.x) <= maxcoor && Mathf.Abs(z) <= maxcoor && HexagnoalMap.Instance.IsReachablePoint(temp) && !closeList.Contains(temp))
        {
            neighborCells.Add(temp);
        }
    }
    
    bool OpenListContains(Vector2Int coor,out int idx)
    {

        Vector2IntComparer comparer = Vector2IntComparer.GetComperer();
        for (int i = 0; i < openList.Count; i++)
        {
            if (comparer.Equals(openList[i].Coordinate, coor))
            {
                idx = i;
                return true;
            }
        }
        idx = -1;
        return false;
    }
}


